Add memory map to the debugger backend (#96)#1113
Merged
Merged
Conversation
Member
Author
|
Added a Debugger Memory Map sidebar widget (commit c9416c0) so the map can be exercised in the UI, not just via
How to test: launch/attach a target (LLDB or GDB), pause it, open the Debugger Memory Map sidebar icon. Compare against |
Introduce a memory-map abstraction that exposes the target's mapped
virtual-address regions with their permissions, mirroring the existing
modules feature end to end.
A memory region is distinct from a module: a module (one entry per loaded
binary) typically spans several regions with different permissions, and
many regions (stack, heap, anonymous mmap) belong to no module at all. So
this is a new `DebugMemoryRegion` type and a new `GetMemoryMap()` adapter
API rather than an extension of the module list.
Layers added:
- core: DebugMemoryRegion struct + DebugAdapter::GetMemoryMap() (default
empty so adapters opt in), a DebuggerMemoryMap state cache that is
marked dirty on every stop and fetched lazily, and
DebuggerController::GetMemoryMap().
- adapters: LLDB (SBProcess::GetMemoryRegions) and GDB (/proc/[pid]/maps,
parsed per region instead of aggregated by path as GetModuleList does).
- FFI: BNDebugMemoryRegion + BNDebuggerGetMemoryMap/FreeMemoryRegions.
- C++ API + Python (`controller.memory_map`).
This is the data foundation for #96/#27. It deliberately does not yet
change the debugger's live BinaryView: the flat remote region added in
CreateDebuggerBinaryView still backs reads, so readability is unaffected.
Replacing that flat region with per-region segments (lengths + rwx) is a
follow-up, to be landed only after the map is validated as complete on each
platform so that no readable byte becomes unreadable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a "Debugger Memory Map" sidebar panel that lists the target's mapped regions (Start / End / Size / Permissions / Name), mirroring the existing Debugger Modules panel. It refreshes on the target-stopped event via DebuggerController::GetMemoryMap(), supports filtering, copy/copy-all, and double-click / context-menu navigation to a region's start or end. This gives a UI to exercise and validate the memory-map data added in the previous commit before the per-region-segment follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enumerate the target's committed virtual-memory regions via IDebugDataSpaces2::QueryVirtual (the DbgEng wrapper over VirtualQueryEx), walking the address space from 0 and advancing by each region's size until QueryVirtual reports the end. Only MEM_COMMIT regions that are neither guard nor no-access pages are reported. Permissions are derived from the page Protect flags; MEM_MAPPED regions are marked shared; and image-backed (MEM_IMAGE) regions get their backing module's image path as the name via GetModuleByOffset/ GetModuleNames. The DbgEng TTD and kernel adapters inherit this via DbgEngAdapter. The non-DbgEng WindowsNativeAdapter still uses the default empty map for now. Note: this adapter only builds on Windows, so it is validated by CI rather than the local (macOS) build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enumerate the target's committed virtual-memory regions with VirtualQueryEx on the process handle, walking the address space from 0 and advancing by each region's size until the query reports the end. Mirrors the DbgEng implementation: only MEM_COMMIT regions that are neither guard nor no-access pages are reported; permissions come from the page Protect flags; MEM_MAPPED regions are marked shared; and image/file-backed regions get their backing file path via the existing GetModuleNameFromHandle helper (the "<unknown>" sentinel is mapped to an empty name for anonymous mappings). With this, all Windows adapters (DbgEng + TTD + kernel via inheritance, and WindowsNative) report a memory map. Note: this adapter only builds on Windows, so it is validated by CI rather than the local (macOS) build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GdbMiAdapter derives directly from DebugAdapter (not GdbAdapter), so it did not inherit the RSP adapter's memory map and was returning the empty default. Implement it by parsing "info proc mappings" -- the same command it already uses for GetModuleList -- but keeping every region and reading the permissions. The Perms column is present on GDB 8.0+ and its position can vary, so it is detected by pattern rather than fixed index; on older GDB that omits it, the region is still reported as readable. The trailing objfile/pseudo-name (e.g. [stack]) is used as the region name. Builds locally (this adapter is compiled on macOS). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add "Select In Binary View" context action: navigates to the region's start and selects its full [start, end) range in the current view. - Add "Save Region To Disk..." context action: reads the region via ReadMemory and writes it to a user-chosen file, warning on partial reads and erroring when nothing is readable. - Fix Copy so that a multi-cell selection copies every selected cell (grouped by row, tab-separated columns, newline-separated rows, in visual order) instead of only the first cell. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e1e7a43 to
7ee5bc5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First step toward #96 (and #27): expose the target's memory map — every mapped region of the virtual address space with its permissions — through the debugger, mirroring the existing modules feature end to end.
A memory region is deliberately a separate abstraction from a module, not an extension of it:
.textr-x,.datarw-, …);mmap— belong to no module at all;__TEXTinto one region and shares a single__LINKEDIT), so neither can be cleanly derived from the other.So this adds a new
DebugMemoryRegiontype and a newGetMemoryMap()adapter API rather than overloadingDebugModule.What's included
DebugMemoryRegionstruct;DebugAdapter::GetMemoryMap()(default-empty so adapters opt in incrementally);DebuggerMemoryMapstate cache (marked dirty on every stop, fetched lazily);DebuggerController::GetMemoryMap()SBProcess::GetMemoryRegions(); GDB via/proc/[pid]/maps, parsed per region (with rwx/shared) instead of aggregated by path the wayGetModuleListdoesBNDebugMemoryRegion+BNDebuggerGetMemoryMap/BNDebuggerFreeMemoryRegionsDebuggerController::GetMemoryMap()DebugMemoryRegion+controller.memory_mapDebugMemoryRegioncarries{start, size, name, read, write, execute, shared}, wherenameis a backing file path, a pseudo-name like[stack]/[heap], or empty for anonymous mappings.Scope / deliberately out of scope
This is the data foundation only. It does not change the debugger's live
BinaryView: the flat remote region added inCreateDebuggerBinaryViewstill backs reads, so readability is unaffected on every platform.The follow-up — replacing that flat region with per-region segments (real lengths + rwx permissions), which is the behavioral goal of #96/#27 — is intentionally a separate PR. Reads today go straight to the adapter (
DebuggerFileAccessor::Read→ReadMemory) and are not gated by segments; the flip to per-region backing should land only after the map is validated as complete on each platform, so that no byte that is actually readable becomes unreadable. Landing the map first makes that validation possible (open the panel / callcontroller.memory_mapand compare against real readability).Adapters other than LLDB/GDB (DbgEng, etc.) inherit the default-empty
GetMemoryMap()and return an empty map until implemented — no behavior change for them.Testing
ninja debuggercore debuggerapi) on macOS.🤖 Generated with Claude Code